package com.example.demo.util;

import net.sf.json.JSONObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.cookie.Cookie;
import org.apache.http.cookie.CookieOrigin;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.impl.cookie.BestMatchSpec;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.*;
import java.io.*;
import java.net.*;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.Map.Entry;

/**
 * 
 * 类说明：信任所有的证书
 *
 * @author cyp
 * @date 2015年9月15日 下午4:37:52
 */
class AnyTrustStrategy implements TrustStrategy {

  public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    return true;
  }

}

/**
 *
 * 类说明：http工具类
 *
 * @author cyp
 * @date 2015年9月15日 下午4:38:14
 */
public class HttpsUtil {

  private static final Log log = LogFactory.getLog(HttpsUtil.class);

  private static int bufferSize = 1024;

  private static volatile HttpsUtil instance;

  private ConnectionConfig connConfig;

  private SocketConfig socketConfig;

  private ConnectionSocketFactory plainSF;

  private KeyStore trustStore;

  private SSLContext sslContext;

  private LayeredConnectionSocketFactory sslSF;

  private Registry<ConnectionSocketFactory> registry;

  private PoolingHttpClientConnectionManager connManager;

  private volatile HttpClient client;

  private volatile BasicCookieStore cookieStore;

  public static String defaultEncoding = "utf-8";

  /**
   * @Title paramsConverter
   * @Description map转换为表单参数
   * @author cyp
   * @date 2015年9月15日 下午4:39:20
   * @param params
   * @return
   * @return List<NameValuePair>
   */
  private static List<NameValuePair> paramsConverter(Map<String, String> params) {
    List<NameValuePair> nvps = new LinkedList<NameValuePair>();
    Set<Entry<String, String>> paramsSet = params.entrySet();
    for (Entry<String, String> paramEntry : paramsSet) {
      nvps.add(new BasicNameValuePair(paramEntry.getKey(), paramEntry.getValue()));
    }
    return nvps;
  }

  /**
   * @Title readStream
   * @Description 流转为字符串
   * @author cyp
   * @date 2015年9月15日 下午4:40:16
   * @param in
   * @param encoding
   * @return
   * @return String
   */
  public static String readStream(InputStream in, String encoding) {
    if (in == null) {
      return null;
    }
    try {
      InputStreamReader inReader = null;
      if (encoding == null) {
        inReader = new InputStreamReader(in, defaultEncoding);
      } else {
        inReader = new InputStreamReader(in, encoding);
      }
      char[] buffer = new char[bufferSize];
      int readLen = 0;
      StringBuffer sb = new StringBuffer();
      while ((readLen = inReader.read(buffer)) != -1) {
        sb.append(buffer, 0, readLen);
      }
      inReader.close();
      return sb.toString();
    } catch (IOException e) {
      log.error("读取返回内容出错", e);
    }
    return null;
  }

  /**
   * 私有构造在生成client时注入SSL
   */
  private HttpsUtil() {
    // 设置连接参数
    connConfig = ConnectionConfig.custom().setCharset(Charset.forName(defaultEncoding)).build();
    socketConfig = SocketConfig.custom().setSoTimeout(100000).build();
    RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder
        .<ConnectionSocketFactory> create();
    plainSF = new PlainConnectionSocketFactory();
    registryBuilder.register("http", plainSF);
    // 指定信任密钥存储对象和连接套接字工厂
    try {
      trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
      sslContext = SSLContexts.custom().useTLS()
          .loadTrustMaterial(trustStore, new AnyTrustStrategy()).build();
      sslSF = new SSLConnectionSocketFactory(sslContext,
          SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
      registryBuilder.register("https", sslSF);
    } catch (KeyStoreException e) {
      throw new RuntimeException(e);
    } catch (KeyManagementException e) {
      throw new RuntimeException(e);
    } catch (NoSuchAlgorithmException e) {
      throw new RuntimeException(e);
    }
    registry = registryBuilder.build();
    // 设置连接管理器
    connManager = new PoolingHttpClientConnectionManager(registry);
    connManager.setDefaultConnectionConfig(connConfig);
    connManager.setDefaultSocketConfig(socketConfig);
    // 指定cookie存储对象
    cookieStore = new BasicCookieStore();
    // 构建客户端
    client = HttpClientBuilder.create().setDefaultCookieStore(cookieStore)
        .setConnectionManager(connManager).build();
  }

  /**
   * @Title getInstance
   * @Description 保证获取唯一的http实例
   * @author cyp
   * @date 2015年9月15日 下午4:41:33
   * @return
   * @return HttpUtil
   */
  public static HttpsUtil getInstance() {
    synchronized (HttpsUtil.class) {
      if (HttpsUtil.instance == null) {
        instance = new HttpsUtil();
      }
      return instance;
    }
  }

  public InputStream doGet(String url) throws URISyntaxException, ClientProtocolException,
      IOException {
    HttpResponse response = this.doGet(url, null);
    return response != null ? response.getEntity().getContent() : null;
  }

  public String doGetForString(String url) throws URISyntaxException, ClientProtocolException,
      IOException {
    return HttpsUtil.readStream(this.doGet(url), null);
  }

  public InputStream doGetForStream(String url, Map<String, String> queryParams)
      throws URISyntaxException, ClientProtocolException, IOException {
    HttpResponse response = this.doGet(url, queryParams);
    return response != null ? response.getEntity().getContent() : null;
  }

  public String doGetForString(String url, Map<String, String> queryParams)
      throws URISyntaxException, ClientProtocolException, IOException {
    return HttpsUtil.readStream(this.doGetForStream(url, queryParams), null);
  }

  /**
   * 基本的Get请求
   * 
   * @param url
   *          请求url
   * @param queryParams
   *          请求头的查询参数
   * @return
   * @throws URISyntaxException
   * @throws IOException
   * @throws ClientProtocolException
   */
  public HttpResponse doGet(String url, Map<String, String> queryParams) throws URISyntaxException,
          ClientProtocolException, IOException {
    HttpGet gm = new HttpGet();
    URIBuilder builder = new URIBuilder(url);
    // 填入查询参数
    if (queryParams != null && !queryParams.isEmpty()) {
      builder.setParameters(HttpsUtil.paramsConverter(queryParams));
    }
    gm.setURI(builder.build());
    return client.execute(gm);
  }

  public InputStream doPostForStream(String url, Map<String, String> queryParams)
      throws URISyntaxException, ClientProtocolException, IOException {
    HttpResponse response = this.doPost(url, queryParams, null);
    return response != null ? response.getEntity().getContent() : null;
  }

  public String doPostForString(String url, Map<String, String> queryParams)
      throws URISyntaxException, ClientProtocolException, IOException {
    return HttpsUtil.readStream(this.doPostForStream(url, queryParams), null);
  }

  public InputStream doPostForStream(String url, Map<String, String> queryParams,
      Map<String, String> formParams) throws URISyntaxException, IllegalStateException, IOException {
    HttpResponse response = this.doPost(url, queryParams, formParams);
    return response != null ? response.getEntity().getContent() : null;
  }

  public String doPostRetString(String url, Map<String, String> queryParams,
      Map<String, String> formParams) throws URISyntaxException, ClientProtocolException,
      IOException {
    return HttpsUtil.readStream(this.doPostForStream(url, queryParams, formParams), null);
  }

  /**
   * 基本的Post请求
   *
   * @param url
   *          请求url
   * @param queryParams
   *          请求头的查询参数
   * @param formParams
   *          post表单的参数
   * @return
   * @throws URISyntaxException
   * @throws IOException
   */
  public HttpResponse doPost(String url, Map<String, String> queryParams,
                             Map<String, String> formParams) throws URISyntaxException, IOException {
		  HttpPost pm = new HttpPost();
		  URIBuilder builder = new URIBuilder(url);
		  // 填入查询参数
		  if (queryParams != null && !queryParams.isEmpty()) {
			  builder.setParameters(HttpsUtil.paramsConverter(queryParams));
		  }
		  pm.setURI(builder.build());
		  // 填入表单参数
		  if (formParams != null && !formParams.isEmpty()) {
			  pm.setEntity(new UrlEncodedFormEntity(HttpsUtil.paramsConverter(formParams)));
		  }
		  return client.execute(pm);
  }

  /**
   * 获取当前Http客户端状态中的Cookie
   *
   * @param domain
   *          作用域
   * @param port
   *          端口 传null 默认80
   * @param path
   *          Cookie路径 传null 默认"/"
   * @param useSecure
   *          Cookie是否采用安全机制 传null 默认false
   * @return
   */
  public Map<String, Cookie> getCookie(String domain, Integer port, String path, Boolean useSecure) {
    if (domain == null) {
      return null;
    }
    if (port == null) {
      port = 80;
    }
    if (path == null) {
      path = "/";
    }
    if (useSecure == null) {
      useSecure = false;
    }
    List<Cookie> cookies = cookieStore.getCookies();
    if (cookies == null || cookies.isEmpty()) {
      return null;
    }

    CookieOrigin origin = new CookieOrigin(domain, port, path, useSecure);
    BestMatchSpec cookieSpec = new BestMatchSpec();
    Map<String, Cookie> retVal = new HashMap<String, Cookie>();
    for (Cookie cookie : cookies) {
      if (cookieSpec.match(cookie, origin)) {
        retVal.put(cookie.getName(), cookie);
      }
    }
    return retVal;
  }

  /**
   * 批量设置Cookie
   *
   * @param cookies
   *          cookie键值对图
   * @param domain
   *          作用域 不可为空
   * @param path
   *          路径 传null默认为"/"
   * @param useSecure
   *          是否使用安全机制 传null 默认为false
   * @return 是否成功设置cookie
   */
  public boolean setCookie(Map<String, String> cookies, String domain, String path,
      Boolean useSecure) {
    synchronized (cookieStore) {
      if (domain == null) {
        return false;
      }
      if (path == null) {
        path = "/";
      }
      if (useSecure == null) {
        useSecure = false;
      }
      if (cookies == null || cookies.isEmpty()) {
        return true;
      }
      Set<Entry<String, String>> set = cookies.entrySet();
      String key = null;
      String value = null;
      for (Entry<String, String> entry : set) {
        key = entry.getKey();
        if (key == null || key.isEmpty() || value == null || value.isEmpty()) {
          throw new IllegalArgumentException("cookies key and value both can not be empty");
        }
        BasicClientCookie cookie = new BasicClientCookie(key, value);
        cookie.setDomain(domain);
        cookie.setPath(path);
        cookie.setSecure(useSecure);
        cookieStore.addCookie(cookie);
      }
      return true;
    }
  }

  /**
   * 设置单个Cookie
   *
   * @param key
   *          Cookie键
   * @param value
   *          Cookie值
   * @param domain
   *          作用域 不可为空
   * @param path
   *          路径 传null默认为"/"
   * @param useSecure
   *          是否使用安全机制 传null 默认为false
   * @return 是否成功设置cookie
   */
  public boolean setCookie(String key, String value, String domain, String path, Boolean useSecure) {
    Map<String, String> cookies = new HashMap<String, String>();
    cookies.put(key, value);
    return setCookie(cookies, domain, path, useSecure);
  }

	/**
     * 发送post请求，参数为json格式
     *
     * @param url
     * @param jsonStr
     * @return
     * @throws URISyntaxException
     * @throws IOException
	 */
	public String doPost(String url, String jsonStr)  throws URISyntaxException, IOException {
		HttpPost httpPost = new HttpPost(url);
		StringEntity se = new StringEntity(jsonStr,"UTF-8");
		se.setContentType("application/json");
		httpPost.setEntity(se);
		HttpResponse response = client.execute(httpPost);
		String retStr = EntityUtils.toString(response.getEntity());
		// String retStr = HttpsUtil.readStream(response.getEntity().getContent(), null);
		return retStr;
	}


	/**
	 * 发送post请求，接收String参数
	 *
	 * @param requestUrl
	 * @param requestMethod
	 * @param bodyStr
	 * @return
	 */
	public String sendPost(String requestUrl, String requestMethod, String bodyStr) {
		StringBuffer buffer = new StringBuffer();
		try {
			// 创建SSLContext对象，并使用我们指定的信任管理器初始�?
			TrustManager[] tm = { new MyX509TrustManager() };
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			// 从上述SSLContext对象中得到SSLSocketFactory对象
			SSLSocketFactory ssf = sslContext.getSocketFactory();

			URL url = new URL(requestUrl);
			HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
			httpUrlConn.setSSLSocketFactory(ssf);

			httpUrlConn.setDoOutput(true);
			httpUrlConn.setDoInput(true);
			httpUrlConn.setUseCaches(false);
			// 设置请求方式（GET/POST�?
			httpUrlConn.setRequestMethod(requestMethod);

			if ("GET".equalsIgnoreCase(requestMethod)) {
				httpUrlConn.connect();
			}

			// 当有数据�?要提交时
			if (bodyStr != null) {
				OutputStream outputStream = httpUrlConn.getOutputStream();
				// 注意编码格式，防止中文乱�?
				outputStream.write(bodyStr.getBytes("UTF-8"));
				outputStream.close();
			}

			// 将返回的输入流转换成字符�?
			InputStream inputStream = httpUrlConn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

			String str = null;
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}

			bufferedReader.close();
			inputStreamReader.close();
			inputStream.close();
			httpUrlConn.disconnect();

		} catch (ConnectException ce) {
			ce.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return buffer.toString();
	}

	public class MyX509TrustManager implements X509TrustManager {
		public X509Certificate[] getAcceptedIssuers() {
			return null;
		}
		public void checkClientTrusted(X509Certificate[] chain, String authType) {
		}
		public void checkServerTrusted(X509Certificate[] chain, String authType) {
		}
	}

  /**
   * 向指定URL发送GET方法的请求
   *
   * @param url
   *            发送请求的URL
   * @param param
   *            请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
   * @return URL 所代表远程资源的响应结果
   */
  public static String sendGet(String url, String param) {
    String result = "";
    BufferedReader in = null;
    try {
      String urlNameString = url + "?" + param;
      URL realUrl = new URL(urlNameString);
      // 打开和URL之间的连接
      URLConnection connection = realUrl.openConnection();
      // 设置通用的请求属性
      connection.setRequestProperty("accept", "*/*");
      connection.setRequestProperty("connection", "Keep-Alive");
      connection.setRequestProperty("user-agent",
              "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
      // 建立实际的连接
      connection.connect();
      // 获取所有响应头字段
      Map<String, List<String>> map = connection.getHeaderFields();
      // 遍历所有的响应头字段
      for (String key : map.keySet()) {
        System.out.println(key + "--->" + map.get(key));
      }
      // 定义 BufferedReader输入流来读取URL的响应
      in = new BufferedReader(new InputStreamReader(
              connection.getInputStream(), "utf-8"));
      String line;
      while ((line = in.readLine()) != null) {
        result += line;
      }
    } catch (Exception e) {
      System.out.println("发送GET请求出现异常！" + e);
      e.printStackTrace();
    }
    // 使用finally块来关闭输入流
    finally {
      try {
        if (in != null) {
          in.close();
        }
      } catch (Exception e2) {
        e2.printStackTrace();
      }
    }
    return result;
  }

  /**
   * post请求
   * @param url         url地址
   * @param jsonParam     参数
   * @param noNeedResponse    不需要返回结果
   * @return
   */
  public static JSONObject httpPost(String url,JSONObject jsonParam, boolean noNeedResponse){
    //post请求返回结果
    DefaultHttpClient httpClient = new DefaultHttpClient();
    JSONObject jsonResult = null;
    HttpPost method = new HttpPost(url);
    try {
      if (null != jsonParam) {
        //解决中文乱码问题
        StringEntity entity = new StringEntity(jsonParam.toString(), "utf-8");
        entity.setContentEncoding("UTF-8");
        entity.setContentType("application/json");
        method.setEntity(entity);
      }
      HttpResponse result = httpClient.execute(method);
      url = URLDecoder.decode(url, "UTF-8");
      /**请求发送成功，并得到响应**/
      if (result.getStatusLine().getStatusCode() == 200) {
        String str = "";
        try {
          /**读取服务器返回过来的json字符串数据**/
          str = EntityUtils.toString(result.getEntity());
          if (noNeedResponse) {
            return null;
          }
          /**把json字符串转换成json对象**/
          jsonResult = JSONObject.fromObject(str);
        } catch (Exception e) {
//          logger.error("post请求提交失败:" + url, e);
        }
      }
    } catch (IOException e) {
//      logger.error("post请求提交失败:" + url, e);
    }
    return jsonResult;
  }

  /**
   * 发送get请求
   * @param url    路径
   * @return
   */
  public static String httpGet(String url){
    //get请求返回结果
    JSONObject jsonResult = null;
      String strResult = null;
    try {
      DefaultHttpClient client = new DefaultHttpClient();
      //发送get请求
      HttpGet request = new HttpGet(url);
      HttpResponse response = client.execute(request);

      /**请求发送成功，并得到响应**/
      if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
        /**读取服务器返回过来的json字符串数据**/
        strResult = EntityUtils.toString(response.getEntity(),"UTF-8");
        /**把json字符串转换成json对象**/
//        jsonResult = JSONObject.fromObject(strResult);
        url = URLDecoder.decode(url, "UTF-8");
      } else {
          log.error("get请求提交失败:" + url);
      }
    } catch (IOException e) {
        log.error("get请求提交失败:" + url, e);
    }
    return strResult;
  }

}
